package com.gitee.wsl.collections.set.api


/**
 * Defines a collection that counts the number of times an object appears in
 * the collection.
 *
 *
 * Suppose you have a MultiSet that contains `{a, a, b, c}`.
 * Calling [.getCount] on `a` would return 2, while
 * calling [.uniqueSet] would return `{a, b, c}`.
 *
 *
 * @param <E> the type held in the multiset
 * @since 4.1
</E> */
interface MultiSet<E> : MutableCollection<E> {
    /**
     * An unmodifiable entry for an element and its occurrence as contained in a MultiSet.
     *
     *
     * The [MultiSet.entrySet] method returns a view of the multiset whose elements
     * implement this interface.
     *
     *
     * @param <E>  the element type
    </E> */
    interface Entry<E> {
        /**
         * Compares the specified object with this entry for equality.
         * Returns true if the given object is also a multiset entry
         * and the two entries represent the same element with the same
         * number of occurrences.
         *
         *
         * More formally, two entries `e1` and `e2` represent
         * the same mapping if
         *
         * <pre>
         * (e1.getElement()==null ? e2.getElement()==null
         * : e1.getElement().equals(e2.getElement())) &amp;&amp;
         * (e1.getCount()==e2.getCount())
        </pre> *
         *
         * @param other object to be compared for equality with this multiset entry
         * @return true if the specified object is equal to this multiset entry
         */
        override fun equals(other: Any?): Boolean

        /**
         * Returns the number of occurrences for the element of this entry.
         *
         * @return the number of occurrences of the element
         */
        val count: Int

        /**
         * Returns the element corresponding to this entry.
         *
         * @return the element corresponding to this entry
         */
        val element: E

        /**
         * Returns the hash code value for this multiset entry.
         *
         *
         * The hash code of a multiset entry `e` is defined to be:
         * <pre>
         * (e==null ? 0 : e.hashCode()) ^ noOccurrences)
        </pre> *
         *
         * @return the hash code value for this multiset entry
         */
        override fun hashCode(): Int
    }

    /**
     * Adds one copy of the specified object to the MultiSet.
     *
     *
     * If the object is already in the [.uniqueSet] then increment its
     * count as reported by [.getCount]. Otherwise, add it to the
     * [.uniqueSet] and report its count as 1.
     *
     *
     * @param element  the object to add
     * @return `true` always, as the size of the MultiSet is increased
     * in any case
     */
     override fun add(element: E): Boolean

    override fun addAll(elements: Collection<E>): Boolean {
        elements.forEach { if(!add(it) ) return false }
        return true
    }

    /**
     * Adds a number of occurrences of the specified object to the MultiSet.
     *
     *
     * If the object is already in the [.uniqueSet] then increment its
     * count as reported by [.getCount]. Otherwise, add it to the
     * [.uniqueSet] and report its count as `occurrences`.
     *
     *
     * @param object  the object to add
     * @param occurrences  the number of occurrences to add, may be zero,
     * in which case no change is made to the multiset
     * @return the number of occurrences of the object in the multiset before
     * this operation; possibly zero
     * @throws IllegalArgumentException if occurrences is negative
     */
    fun add(element: E, occurrences: Int): Int

    /**
     * Returns `true` if the MultiSet contains at least one
     * occurrence for each element contained in the given collection.
     *
     * @param elements  the collection to check against
     * @return `true` if the MultiSet contains all the collection
     */
     override fun containsAll(elements: Collection<E>): Boolean

    /**
     * Returns a [Set] of all entries contained in the MultiSet.
     *
     *
     * The returned set is backed by this multiset, so any change to either
     * is immediately reflected in the other.
     *
     *
     * @return the Set of MultiSet entries
     */
    fun entrySet(): Set<Entry<E>>


    /**
     * Compares this MultiSet to another object.
     *
     *
     * This MultiSet equals another object if it is also a MultiSet
     * that contains the same number of occurrences of the same elements.
     *
     *
     * @param other  the object to compare to
     * @return true if equal
     */
    override fun equals(other: Any?): Boolean

    /**
     * Returns the number of occurrences of the given object currently
     * in the MultiSet. If the object does not exist in the multiset,
     * return 0.
     *
     * @param object  the object to search for
     * @return the number of occurrences of the object, zero if not found
     */
    fun getCount(element: E): Int

    /**
     * Gets a hash code for the MultiSet compatible with the definition of equals.
     * The hash code is defined as the sum total of a hash code for each element.
     * The per element hash code is defined as
     * `(e==null ? 0 : e.hashCode()) ^ noOccurrences)`.
     *
     * @return the hash code of the MultiSet
     */
    override fun hashCode(): Int

    /**
     * Returns an [HashMapIterator] over the entire set of members,
     * including copies due to cardinality. This iterator is fail-fast
     * and will not tolerate concurrent modifications.
     *
     * @return iterator over all elements in the MultiSet
     */
     override fun iterator(): MutableIterator<E>

    /**
     * Removes one occurrence of the given object from the MultiSet.
     *
     *
     * If the number of occurrences after this operation is reduced
     * to zero, the object will be removed from the [.uniqueSet].
     *
     * @param element  the object to remove
     * @return `true` if this call changed the collection
     */
     override fun remove(element: E): Boolean

    /**
     * Removes a number of occurrences of the specified object from the MultiSet.
     *
     *
     * If the number of occurrences to remove is greater than the actual number of
     * occurrences in the multiset, the object will be removed from the multiset.
     *
     *
     * @param object  the object to remove
     * @param occurrences  the number of occurrences to remove, may be zero,
     * in which case no change is made to the multiset
     * @return the number of occurrences of the object in the multiset
     * before the operation; possibly zero
     * @throws IllegalArgumentException if occurrences is negative
     */
    fun remove(`object`: E, occurrences: Int): Int

    /**
     * Remove all occurrences of all elements from this MultiSet represented
     * in the given collection.
     *
     * @param elements  the collection of elements to remove
     * @return `true` if this call changed the multiset
     */
     override fun removeAll(elements: Collection<E>): Boolean

    /**
     * Remove any elements of this MultiSet that are not contained in the
     * given collection.
     *
     * @param elements  the collection of elements to retain
     * @return `true` if this call changed the multiset
     */
     override fun retainAll(elements: Collection<E>): Boolean

    /**
     * Sets the number of occurrences of the specified object in the MultiSet
     * to the given count.
     *
     *
     * If the provided count is zero, the object will be removed from the
     * [.uniqueSet].
     *
     *
     * @param object  the object to update
     * @param count  the number of occurrences of the object
     * @return the number of occurrences of the object before this operation, zero
     * if the object was not contained in the multiset
     * @throws IllegalArgumentException if count is negative
     */
    fun setCount(element: E, count: Int): Int

    /**
     * Returns the total number of items in the MultiSet.
     *
     * @return the total size of the multiset
     */
    // override fun size(): Int
    override val size: Int

    /**
     * Returns a [Set] of unique elements in the MultiSet.
     *
     *
     * Uniqueness constraints are the same as those in [java.util.Set].
     *
     *
     *
     * The returned set is backed by this multiset, so any change to either
     * is immediately reflected in the other. Only removal operations are
     * supported, in which case all occurrences of the element are removed
     * from the backing multiset.
     *
     *
     * @return the Set of unique MultiSet elements
     */
    fun uniqueSet(): Set<E>
}
